package options

import (
	"file-cleaner/internal/pkg/server"
	"github.com/marmotedu/errors"
	"github.com/spf13/pflag"
	"time"
)

type RuntimeOptions struct {
	GoroutinePoolSize uint `json:"goroutine-pool-size" mapstructure:"goroutine-pool-size"`
	GCCycleSeconds    uint `json:"gc-cycle-seconds" mapstructure:"gc-cycle-seconds"`
}

func NewRuntimeOptions() *RuntimeOptions {
	return &RuntimeOptions{
		GoroutinePoolSize: 100,
	}
}

func (r *RuntimeOptions) AddFlags(fs *pflag.FlagSet) {
	fs.UintVar(&r.GoroutinePoolSize, "runtime.goroutine-pool-size", r.GoroutinePoolSize, ""+
		"Set the size of the runtime goroutine pool.")

	fs.UintVar(&r.GCCycleSeconds, "runtime.gc-cycle-seconds", r.GCCycleSeconds, ""+
		"Set the duration of the runtime garbage collection cycle, in seconds.")
}

func (r *RuntimeOptions) Validate() []error {
	var errs []error

	// validate goroutine-pool-size
	limit, err := getSysRLimit()
	if err != nil {
		errs = append(errs, errors.Errorf("--runtime.goroutine-pool-size validate failed, cased by: %+v.", err))
	} else if r.GoroutinePoolSize > uint(limit) {
		errs = append(errs, errors.Errorf("--runtime.goroutine-pool-size exceeds the maximum system support limit(%d).", limit))
	}

	return errs
}

func (r *RuntimeOptions) ApplyTo(c *server.Config) error {
	poolCap, err := r.optimisePoolCap()
	if err != nil {
		return err
	}
	c.Runtime.GoroutinePoolCap = poolCap

	if c.Runtime.GCCycle > 0 {
		c.Runtime.GCCycle = time.Duration(r.GCCycleSeconds) * time.Second
	}

	return nil
}

func (r *RuntimeOptions) optimisePoolCap() (int, error) {
	// Check poolCap is 0, or not
	if r.GoroutinePoolSize < 1 {
		return -1, errors.New("goroutine pool size < 1")
	}

	poolCap := int(r.GoroutinePoolSize) >> 2

	if poolCap < 1 {
		poolCap = 1
	}

	return poolCap, nil
}
